package com.unione.unione_base.util;

import java.util.Arrays;

public class CrcUtil {
    /**
     * 计算字节数组的CRC16校验吗
     *
     * @param bytes 数据字节数组
     * @return 返回2个长度的字节数组，即为CRC16的校验码
     */
    public static byte[] getCRC(byte[] bytes) {
//        ModBus 通信协议的 CRC ( 冗余循环校验码含2个字节, 即 16 位二进制数。
//        CRC 码由发送设备计算, 放置于所发送信息帧的尾部。
//        接收信息设备再重新计算所接收信息 (除 CRC 之外的部分）的 CRC,
//        比较计算得到的 CRC 是否与接收到CRC相符, 如果两者不相符, 则认为数据出错。
//
//        1) 预置 1 个 16 位的寄存器为十六进制FFFF(即全为 1) , 称此寄存器为 CRC寄存器。
//        2) 把第一个 8 位二进制数据 (通信信息帧的第一个字节) 与 16 位的 CRC寄存器的低 8 位相异或, 把结果放于 CRC寄存器。
//        3) 把 CRC 寄存器的内容右移一位( 朝低位)用 0 填补最高位, 并检查右移后的移出位。
//        4) 如果移出位为 0, 重复第 3 步 ( 再次右移一位); 如果移出位为 1, CRC 寄存器与多项式A001 ( 1010 0000 0000 0001) 进行异或。
//        5) 重复步骤 3 和步骤 4, 直到右移 8 次,这样整个8位数据全部进行了处理。
//        6) 重复步骤 2 到步骤 5, 进行通信信息帧下一个字节的处理。
//        7) 将该通信信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低字节进行交换。
//        8) 最后得到的 CRC寄存器内容即为 CRC码。

        int CRC = 0x0000ffff;
        int POLYNOMIAL = 0x0000a001;

        int i, j;
        for (i = 0; i < bytes.length; i++) {
            CRC ^= ((int) bytes[i] & 0x000000ff);
            for (j = 0; j < 8; j++) {
                if ((CRC & 0x00000001) != 0) {
                    CRC >>= 1;
                    CRC ^= POLYNOMIAL;
                } else {
                    CRC >>= 1;
                }
            }
        }

        CRC = ((CRC & 0x0000FF00) >> 8) | ((CRC & 0x000000FF) << 8);

        return new byte[]{
                (byte) (CRC >>> 8),
                (byte) CRC
        };
    }


    public static byte[] getCRC16(byte[] bytes) {
        int crc = 0x0000; // 初始化CRC值为0
        for (int i = 0; i < bytes.length; i++) {
            crc ^= (bytes[i] << 8); // 将当前字节与CRC进行异或操作，并将结果左移8位
            for (int j = 0; j < 8; j++) {
                if ((crc & 0x8000) != 0) { // 如果CRC的最高位为1
                    crc = (crc << 1) ^ 0x1021; // 对CRC进行左移一位并与特定的值进行异或操作
                } else {
                    crc <<= 1; // 再次对CRC进行左移一位
                }
            }
        }
        return ByteUtils.to2BytesMin(crc); // 返回最终的CRC值
    }


//    public static String getCRCHex(byte[] bytes) {
//        byte[] result = getCRC(bytes);
//        return Hex.encodeHexString(result);
//    }

    /**
     * 校验数据是否符合CRC16的校验
     *
     * @param bytes 完整的字节数据，包含CRC16部分
     * @return 校验通过返回true，否则返回false
     */
    public static boolean isValid(byte[] bytes) {
        int len = bytes.length;
        if (len > 2) {
            byte[] data = Arrays.copyOfRange(bytes, 0, len - 2);

            byte[] crc = getCRC(data);
            return crc.length == 2 && crc[0] == bytes[len - 2] && crc[1] == bytes[len - 1];
        }

        return false;
    }

    /**
     * 将 int 类型转为4个字节长度的字节数组
     *
     * @param value int类型的数值
     * @return 返回4个字节长度的字节数组
     */
    public static byte[] intToByteArray(int value) {
        return new byte[]{
                (byte) (value >>> 24),
                (byte) (value >>> 16),
                (byte) (value >>> 8),
                (byte) value};
    }


}
